//! 身份证结构和相关处理
//!
//! 提供了一个`IdentityCard`。
//! 通过身份证字符串解析为`IdentityCard`
//! 增加了一个验证当前结构的方法

use crate::codes::{SIGN_CODES, VERIFY_CODES};
use std::{error::Error, fmt, str::FromStr, string::ToString};

#[derive(Debug)]
pub struct ParseIcError {
    err: String,
}

impl Error for ParseIcError {}

impl fmt::Display for ParseIcError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "身份证错误：{}", self.err)
    }
    // add code here
}

/// 身份证的结构类型
///
/// 创建一个身份证 struct 分别包含地区，生日，序号，和校验码
#[derive(Debug)]
pub struct IdentityCard {
    pub region: String,
    pub birthday: String,
    pub seq: String,
    pub verify: String,
}

impl IdentityCard {
    /// 验证身份证是否正确
    pub fn verify_ic(&self) -> bool {
        if self.region.len() != 6 {
            return false;
        }
        if self.birthday.len() != 8 {
            return false;
        }
        if self.seq.len() != 3 {
            return false;
        }
        if self.verify.len() != 1 {
            return false;
        }

        let verify_code = compute_verify_code(&self.region, &self.birthday, &self.seq).to_string();

        verify_code.eq(&self.verify)
    }
}

impl fmt::Display for IdentityCard {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}{}{}{}",
            self.region, self.birthday, self.seq, self.verify
        )
    }
}

impl FromStr for IdentityCard {
    type Err = ParseIcError;

    /// 解析身份证字符串 `&str`
    ///
    /// ```
    /// use std::str::FromStr;
    /// let ic = IdentityCard.from_str("440222199602175492").unwrap();
    ///
    /// assert_eq!(ic.verify_ic(), true);
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let ic_number: String = String::from(s);
        if ic_number.len() != 18 {
            return Err(ParseIcError {
                err: "身份证长度必须是18位".to_string(),
            });
        }
        let ic = IdentityCard {
            region: ic_number[..6].to_string(),
            birthday: ic_number[6..14].to_string(),
            seq: ic_number[14..17].to_string(),
            verify: ic_number[17..].to_string(),
        };

        Ok(ic)
    }
}

/// 计算身份证的验证码
///
/// 身份证的最后以为是验证码，通过计算获取
pub fn compute_verify_code(region: &String, birthday: &String, seq: &String) -> char {
    let precode = format!("{}{}{}", region, birthday, seq);
    let mut sum: i32 = 0;
    let mut i = 0;
    for c in precode.chars() {
        let n: i32 = c.to_string().parse().unwrap();
        sum = sum + SIGN_CODES[i] * n;
        i = i + 1;
    }
    VERIFY_CODES[(sum % 11) as usize]
}
